﻿/*
 * Сделано в SharpDevelop.
 * Пользователь: maxxadm
 * Дата: 03.04.2012
 * Время: 16:10
 * 
 * Для изменения этого шаблона используйте Сервис | Настройка | Кодирование | Правка стандартных заголовков.
 */
using System;
using System.ComponentModel;
using System.Net;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
using System.Text;

//using Un4seen.Bass;
//using Un4seen.Bass.AddOn.Tags;


using TagLib;

namespace mxplay
{
	/*
	 * TODO:
	 * Надо бы реализовать ICustomTypeDescriptor для PlaylistEntry
	 * и плясать от него
	 */
	
	/// <summary>
	/// Description of PlayListBind.
	/// </summary>
	internal class PlayListBind
		:IBindingList
	{
		private List<PlayListBindEntry> m_list=new List<PlayListBindEntry>();
		
		protected void OnListChanged(ListChangedEventArgs e)
		{
			#if DEBUG
			Program.LogMessage(string.Format("Binding list changed, change type = {0}",e.ListChangedType));
			#endif
			
			if(ListChanged!=null) ListChanged(this,e);
		}
		public void AddFile(string file_name)
		{
			Add(new PlayListBindEntry(file_name));
		}
		
		#region ctor
		public PlayListBind()
		{
			init_bindinglist_props();
			
		}
		
		private void init_bindinglist_props()
		{
			SupportsChangeNotification=true;
			SupportsSearching=true;
			SupportsSorting=true;
		}
		#endregion
		
		#region IBindingList implementation
		public bool AllowEdit{get{return false;}}
		public bool AllowNew{get{return false;}}
		public bool AllowRemove{get{return true;}}
		public int Count{get{return m_list.Count;}}
		public bool IsFixedSize{get{return false;}}
		public bool IsReadOnly{get{return false;}}
		public bool IsSorted
		{
			get;
			private set;
		}
		public bool IsSynchronized{get{return false;}}
		public object this[int index]
		{
			get{return m_list[index];}
			set
			{
				PlayListBindEntry new_entry=value as PlayListBindEntry;
				if(new_entry!=null)
					m_list[index]=new_entry;
				else
					throw new ArgumentOutOfRangeException("value");
			}
		}
		public ListSortDirection SortDirection
		{
			get;
			private set;
		}
		public PropertyDescriptor SortProperty
		{
			get;
			private set;
		}
		public bool SupportsChangeNotification
		{
			get;
			private set;
		}
		public bool SupportsSearching
		{
			get;
			private set;
		}
		public bool SupportsSorting
		{
			get;
			private set;
		}
		private object m_syncroot=new object();
		public object SyncRoot { get{return m_syncroot;} }
		public int Add(object value)
		{
			PlayListBindEntry new_entry=value as PlayListBindEntry;
			if(new_entry==null) throw new ArgumentOutOfRangeException("value");
			return Add(new_entry);
		}
		public void AddIndex(PropertyDescriptor property){}
		public object AddNew(){throw new NotSupportedException();}
		public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
		{
			SortProperty=property;
			SortDirection=direction;
			
			m_list.Sort(comparison_internal);
			IsSorted=true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset,-1));
		}
		public void Clear()
		{
			m_list.Clear();
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset,-1));
		}
		public bool Contains(object value)
		{
			if(value is PlayListBindEntry)
			{
				return Contains(value as PlayListBindEntry);
			}
			else
			{
				return false;
			}
		}
		public void CopyTo(Array array,int index)
		{
			(m_list as IList).CopyTo(array, index);
		}
		public int Find(PropertyDescriptor property,object key)
		{
			//тупая реализация
			//так то можно сделать поиск по отдельным свойствам... потом
			return m_list.FindIndex
				(entry => object.Equals(property.GetValue(entry),key));
		}
		public IEnumerator GetEnumerator()
		{
			return (m_list as IEnumerable).GetEnumerator();
		}
		public int IndexOf(object value)
		{
			return m_list.IndexOf(value as PlayListBindEntry);
		}
		public void Insert(int index,object value)
		{
			if(value is PlayListBindEntry)
			{
				m_list.Insert(index,value as PlayListBindEntry);
				(value as PlayListBindEntry).OriginalOrder=index;
				OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded,index));
			}
			else
			{
				throw new ArgumentOutOfRangeException("value");
			}
		}
		public void Remove(object value)
		{
			int rem_index=IndexOf(value);
			if(rem_index>=0) m_list.Remove(value as PlayListBindEntry);
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted,rem_index));
		}
		public void RemoveAt(int index)
		{
			m_list.RemoveAt(index);
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted,index));
		}
		public void RemoveIndex(PropertyDescriptor property){}
		public void RemoveSort()
		{
			/*
			 * На самом деле надо бы сохранять в элементах оригинальную позицию
			 * и сортировать в этом случае по ней
			 * Так и сделано
			 */
			IsSorted=false;
			SortProperty=null;
			m_list.Sort
				((x,y) => x.OriginalOrder-y.OriginalOrder);
		}
		//TODO написать реализацию событий
		public event ListChangedEventHandler ListChanged;
		#endregion
		
		#region sort implementation
		/*
		 * TODO: Можно и потом дописать будет реализацию
		 * для каждого свойства
		 */
		private int comparison_internal(PlayListBindEntry x,PlayListBindEntry y)
		{
			int direction=SortDirection==ListSortDirection.Ascending?1:-1;
			
			if((x==null)&&(y!=null))return -1*direction;
			if((y==null)&&(x!=null))return 1*direction;
			if((x==null)&&(y==null))return 0;
			
			//написать осталось, только и всего
			switch (SortProperty.Name)
			{
				case "Album":
					//Принимаеи во внимание AlbumSort
					string album_x=string.IsNullOrEmpty(x.Tag.AlbumSort)?x.Tag.Album:x.Tag.AlbumSort;
					string album_y=string.IsNullOrEmpty(y.Tag.AlbumSort)?y.Tag.Album:y.Tag.AlbumSort;
					return string.Compare(album_x,album_y,true)*direction;
					
				case "AlbumAtrists":
					string albumatrist_x=null;
					if(!string.IsNullOrEmpty(x.Tag.FirstAlbumArtistSort))
						albumatrist_x=x.Tag.FirstAlbumArtistSort;
					else if(!string.IsNullOrEmpty(x.Tag.FirstAlbumArtist))
						albumatrist_x=x.Tag.FirstAlbumArtist;
					
					string albumatrist_y=null;
					if(!string.IsNullOrEmpty(y.Tag.FirstAlbumArtistSort))
						albumatrist_y=y.Tag.FirstAlbumArtistSort;
					else if(!string.IsNullOrEmpty(y.Tag.FirstAlbumArtist))
						albumatrist_y=y.Tag.FirstAlbumArtist;
					
					return string.Compare(albumatrist_x,albumatrist_y,true)*direction;
				default:
					object obj_x=SortProperty.GetValue(x);
					object obj_y=SortProperty.GetValue(y);
					
					if(obj_x==null)
					{
						if(obj_y==null)
							return 0;
						else
							return -1*direction;
					}
					else if(obj_y==null)
						return 1*direction;
					
					if(obj_x is IComparable)
						return (obj_x as IComparable).CompareTo(obj_y)*direction;
					
					//TODO здесь нет обобщённой реализации
					return 0;
			}
		}

		
		#endregion
		
		#region typed list members
		public int Add(PlayListBindEntry value)
		{
			int ret=m_list.Count;
			try
			{
				m_list.Add(value);
				value.OriginalOrder=ret;
				OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded,ret));
				IsSorted=false;
				return ret;
			}
			catch(Exception ex){throw ex;}
		}
		public bool Contains(PlayListBindEntry value)
		{
			return m_list.Contains(value);
		}
		#endregion
		
		
		
		
	}
	
	public class PlayListBindEntry:ICustomTypeDescriptor
	{
		#region ICustomTypeDescriptor implementation
		public AttributeCollection GetAttributes()
		{
			/*Implementers can return an empty AttributeCollection
			if no custom attributes are specified for an object.
			 */
			return AttributeCollection.Empty;
		}
		public string GetClassName()
		{
			return null;
		}
		public string GetComponentName()
		{
			return null;
		}
		public TypeConverter GetConverter()
		{
			return null;
		}
		public EventDescriptor GetDefaultEvent()
		{
			return null;
		}
		public PropertyDescriptor GetDefaultProperty()
		{
			return null;
		}
		public Object GetEditor(Type editorBaseType)
		{
			return null;
		}
		public EventDescriptorCollection GetEvents()
		{
			return EventDescriptorCollection.Empty;
		}
		public EventDescriptorCollection GetEvents(Attribute[] attributes)
		{
			return EventDescriptorCollection.Empty;
		}
		public PropertyDescriptorCollection GetProperties()
		{
			return PlayListBindOptions.VisibleTagProperties;
		}
		public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
		{
			//DataGridView при автогенерации столбцов 
			//запрашивает свойства у которых BrowsableAttribute = true
//			return PlayListBindOptions.GetVisibleTagProperties(attributes);
			return GetProperties();
		}
		public Object GetPropertyOwner(PropertyDescriptor pd)
		{
			return this;
			
		}
		#endregion
		
		[Browsable(false)]
		public Tag Tag
		{
			get;private set;
		}
		
		[Browsable(false)]
		public File TagFile
		{
			get;private set;
		}
		
		[Browsable(false)]
		public int OriginalOrder
		{
			get; set;
		}
		
		#region ctor
		public PlayListBindEntry(Tag tag)
		{
			if (tag == null)
				throw new ArgumentNullException("tag");
			this.Tag = tag;
		}
		
		public PlayListBindEntry(string FileName)
		{
			if(string.IsNullOrEmpty(FileName)) throw new ArgumentNullException("Filename");
			TagFile=File.Create(FileName);
			Tag=TagFile.Tag;
		}
		
		public PlayListBindEntry()
		{
			//tag is null!
		}
		#endregion
		
		#region Wrapper
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_album",typeof(lang))]
		public string Album
		{
			get
			{	
				if(Tag==null)return string.Empty;
				return Tag.Album;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_albumartist",typeof(lang))]
		public string AlbumArtists
		{
			get
			{
				if(Tag==null)return string.Empty;
				return Tag.JoinedAlbumArtists;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_amazonid",typeof(lang))]
		public string AmazonId
		{
			get
			{
				if(Tag==null) return string.Empty;
				return Tag.AmazonId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_artist",typeof(lang))]
		public string Artists
		{
			get
			{
				if(Tag==null) return string.Empty;
				return Tag.JoinedArtists;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_bpm",typeof(lang))]
		public uint BeatsPerMinute
		{
			get
			{
				if(Tag==null) return 0;
				return Tag.BeatsPerMinute;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_comment",typeof(lang))]
		public string Comment
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.Comment;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_composer",typeof(lang))]
		public string Composers
		{
			get
			{
				if(Tag==null) return string.Empty;
				return Tag.JoinedComposers;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_conductor",typeof(lang))]
		public string Conductor
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.Conductor;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_copyright",typeof(lang))]
		public string Copyright
		{
			get
			{
				if(Tag==null) return string.Empty;
				return Tag.Copyright;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_disc",typeof(lang))]
		public uint Disc
		{
			get
			{
				if (Tag==null) return 0;
				return Tag.Disc;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_disccount",typeof(lang))]
		public uint DiscCount
		{
			get
			{
				if (Tag==null) return 0;
				return Tag.DiscCount;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_genre",typeof(lang))]
		public string Genres
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.JoinedGenres;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_grouping",typeof(lang))]
		public string Grouping
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.Grouping;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_lyrics",typeof(lang))]
		public string Lyrics
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.Lyrics;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_artistid",typeof(lang))]
		public string MusicBrainzArtistId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzArtistId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_discid",typeof(lang))]
		public string MusicBrainzDiscId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzDiscId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_releaseartistid",typeof(lang))]
		public string MusicBrainzReleaseArtistId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzReleaseArtistId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_releascountry",typeof(lang))]
		public string MusicBrainzReleaseCountry
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzReleaseCountry;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_releaseid",typeof(lang))]
		public string MusicBrainzReleaseId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzReleaseId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_releasestatus",typeof(lang))]
		public string MusicBrainzReleaseStatus
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzReleaseStatus;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_releasetype",typeof(lang))]
		public string MusicBrainzReleaseType
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzReleaseType;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_mb_trackid",typeof(lang))]
		public string MusicBrainzTrackId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicBrainzTrackId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_musicipid",typeof(lang))]
		public string MusicIpId
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.MusicIpId;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_performers",typeof(lang))]
		public string Performers
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.JoinedPerformers;
			}
		}
		
		//Что-то с этим нужно будет делать, чтобы
		//гуи знало как это IPciture показывать
		[Browsable(false)]
		public IPicture[] Pictures
		{
			get
			{
				if (Tag==null) return null;
				return Tag.Pictures;
			}
		}
		
		[Browsable(true)]
		public TagTypes TagTypes
		{
			get
			{
				if (Tag==null) return TagTypes.None;
				return Tag.TagTypes;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_title",typeof(lang))]
		public string Title
		{
			get
			{
				if (Tag==null) return string.Empty;
				return Tag.Title;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_track",typeof(lang))]
		public uint Track
		{
			get
			{
				if(Tag==null) return 0;
				return Tag.Track;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_trackcount",typeof(lang))]
		public uint TrackCount
		{
			get
			{
				if (Tag==null) return 0;
				return Tag.TrackCount;
			}
		}
		
		[Browsable(true)]
		[DisplayNameLocalized("tag_year",typeof(lang))]
		public uint Year
		{
			get
			{
				if (Tag==null) return 0;
				return Tag.Year;
			}
		}
		#endregion
		
		
	}
}
